﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

namespace Simulacion.Fruits
{
    public class Fruit
    {
        protected int _life;
        protected double _WholeSPrice;
        protected double _RetailPrice;
        protected double _Inventary;
        protected double _deadProduct;
        protected int _ndoa;
        protected double _ndka;
        protected int _ndopc;
        protected DataTable _orders;
        protected List<string> _dayToOrder;
        protected double _costoOrdenFijo;
        protected double _costoMantener;
        protected double _costoAgotado;
        protected double _costoExtraordinario;
        protected double _CTF;

        protected int _days;

        protected List<Pedido<double, int>> listInventary;

        protected double _pLowMin, _pLowMax, _pJulMin, _pJulMax, _pHighMin, _pHighMax;

        public Fruit()
        {
            listInventary = new List<Pedido<double, int>>();
            _days = 0;
            _ndoa = 0;
            _ndka = 0;
            _deadProduct = 0;
            _orders = new DataTable("Orders");
            _orders.Columns.Add("Cantidad", typeof(double));
            _orders.Columns.Add("Dia", typeof(string));
        }

        public int Life
        {
            get { return _life; }
            set { _life = value; }
        }

        public double WholeSPrice
        {
            get { return _WholeSPrice; }
            set { _WholeSPrice = value; }
        }

        public double CTF
        {
            get { return _CTF; }
        }

        public double RetailPrice
        {
            get { return _RetailPrice; }
            set { _RetailPrice = value; }
        }

        public double CostoOrdenFijo
        {
            get { return _costoOrdenFijo; }
            set { _costoOrdenFijo = value; }
        }

        public double CostoMantener
        {
            get { return _costoMantener; }
            set { _costoMantener = value; }
        }

        public double CostoAgotado
        {
            get { return _costoAgotado; }
            set { _costoAgotado = value; }
        }

        public double CostoExtraordinario
        {
            get { return _costoExtraordinario; }
            set { _costoExtraordinario = value; }
        }

        public double Inventary
        {
            get { return _Inventary; }
        }

        public double NDKA
        {
            get { return _ndka; }
        }

        public int NDOA
        {
            get { return _ndoa; }
        }

        public int NDOPC
        {
            get { return _ndopc; }
        }

        public DataTable Orders
        {
            get
            {
                return this._orders;
            }
            set
            {
                this._orders = value;
            }
        }

        public List<string> DayToOrder
        {
            set
            {
                this._dayToOrder = value;
            }
        }

        public double AddInventary
        {
            set
            {
                _Inventary += value;
                listInventary.Add(new Pedido<double,int>(value, Life)); // First = value, Second = life
            }
        }

        public double pLowMin
        {
            get { return _pLowMin; }
            set { _pLowMin = value; }
        }

        public double pLowMax
        {
            get { return _pLowMax; }
            set { _pLowMax = value; }
        }

        public double pJulMin
        {
            get { return _pJulMin; }
            set { _pJulMin = value; }
        }

        public double pJulMax
        {
            get { return _pJulMax; }
            set { _pJulMax = value; }
        }

        public double pHighMin
        {
            get { return _pHighMin; }
            set { _pHighMin = value; }
        }

        public double pHighMax
        {
            get { return _pHighMax; }
            set { _pHighMax = value; }
        }


        private void BuyFruitExtraDay(DateTime date)
        {
            if (Inventary <= 0)
            {
                foreach (DataRow row in this._orders.Rows)
                {
                    double quantity = (double)row["Cantidad"] / 2;
                    this.AddInventary = quantity;

                    //x Luis //_CTF = _CTF + CostoExtraordinario * RetailPrice;
                    //ok Ramon //
                    _CTF = _CTF + CostoExtraordinario + quantity * RetailPrice;
                    break;
                }
            }
        }

        /// <summary>
        /// Buy fruit, updates the inventary if the current date is valid to order more fruit.
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        private bool BuyFruitIfValidDay(DateTime date)
        {
            bool isValid = false;
            string day;

            foreach (DataRow row in this._orders.Rows)
            {
                day = row["Dia"].ToString();
                if (date.DayOfWeek == DayOfWeek.Monday && day == Day.Lunes.ToString())
                {
                    isValid = true;
                }
                else if (date.DayOfWeek == DayOfWeek.Tuesday && day == Day.Martes.ToString())
                {
                    isValid = true;
                }
                else if (date.DayOfWeek == DayOfWeek.Wednesday && day == Day.Miercoles.ToString())
                {
                    isValid = true;
                }
                else if (date.DayOfWeek == DayOfWeek.Thursday && day == Day.Jueves.ToString())
                {
                    isValid = true;
                }
                else if (date.DayOfWeek == DayOfWeek.Friday && day == Day.Viernes.ToString())
                {
                    isValid = true;

                }
                else if (date.DayOfWeek == DayOfWeek.Saturday && day == Day.Sabado.ToString())
                {
                    isValid = true;
                }
                else if (date.DayOfWeek == DayOfWeek.Sunday && day == Day.Domingo.ToString())
                {
                    isValid = true;
                }

                //_CTF = _CTF + CostoOrdenFijo / 12;

                if (isValid)
                {
                    if (this.Inventary < (double)row["Cantidad"])
                    {
                        double quantity = (double)row["Cantidad"] - this.Inventary;

                        this.AddInventary = quantity;

                        _CTF = _CTF + CostoOrdenFijo/12 + quantity * this.WholeSPrice;
                    }

                    break;
                }
            }

            return isValid;
        }

        public double DeadProduct
        {
            get { return _deadProduct; }
        }

        protected virtual double GetVarPeriodLow()
        {
            return 0;
        }

        protected virtual double GetVarPeriodHigh()
        {
            return 0;
        }

        protected virtual double GetVarPeriodJuly()
        {
            return 0;
        }

        protected virtual double GetFPeriodLow(double x)
        {
            return 0;
        }

        protected virtual double GetFPeriodHigh(double x)
        {
            return 0;
        }

        protected virtual double GetFPeriodJuly(double x)
        {
            return 0;
        }

        protected void UpdateInventary(double request, DateTime date)
        {
            if (_Inventary - request >= 0)
            {
                _Inventary -= request;
            }
            else
            {
                this._ndka += request - _Inventary;
                this._ndoa++;
                //x Luis //_CTF = _CTF + CostoMantener * Math.Abs(Inventary);
                // ok Ramon //
                _CTF = _CTF + CostoAgotado * Math.Abs(request - _Inventary);

                _Inventary = 0;
            }

            //ok Ramon //
            _CTF = _CTF + CostoMantener;

            // Remove request from the Inventary List
            while(listInventary.Count > 0 && request > 0)
            {
                double minimum = Math.Min(listInventary[0].Cantidad, request);
                request -= minimum;
                listInventary[0].Cantidad -= minimum;
                if (listInventary[0].Cantidad == 0) listInventary.RemoveAt(0);
            }
        }

        protected void RemoveDeadProduct(DateTime date)
        {
            for (int i = 0; i < listInventary.Count; i++)
            {
                if (listInventary[i].Life <= 0)
                {
                    _deadProduct += listInventary[i].Cantidad;
                    _ndopc++;

                    _CTF += (_RetailPrice * listInventary[i].Cantidad);

                    _Inventary -= listInventary[i].Cantidad;
                    listInventary.RemoveAt(i);
                    i--;
                }
            }
        }

        /// <summary>
        /// Gets the demand of the fruit.
        /// </summary>
        /// <param name="date">Date to get the demand.</param>
        /// <returns>The value in Kg.</returns>
        public double GetDemand(DateTime date)
        {
            //increase days of simulation
            _days++;
            //get deman for current date
            double request = 
            (date.Month <= 3 || date.Month >= 11)? GetVarPeriodLow() : 
            ((date.Month == 7) ? GetVarPeriodJuly() : GetVarPeriodHigh());

            //update inventary substracting request
            UpdateInventary(request, date);

            // Decrease life of all orders
            for (int i = 0; i < listInventary.Count; i++) listInventary[i].Life--;
            //verify if there are dead product and remove ir
            RemoveDeadProduct(date);

            this.BuyFruitIfValidDay(date);
            this.BuyFruitExtraDay(date);

            return request;
        }

        /// <summary>
        /// Generate a demand variable
        /// </summary>
        /// <param name="date">Date to get the demand.</param>
        /// <returns>The value in Kg.</returns>
        public double GenerateDemand(DateTime date)
        {
            //get demand for current date
            double request =
            (date.Month <= 3 || date.Month >= 11) ? GetVarPeriodLow() :
            ((date.Month == 7) ? GetVarPeriodJuly() : GetVarPeriodHigh());

            return request;
        }

        /// <summary>
        /// Generate a function value
        /// </summary>
        /// <param name="date">Demand value</param>
        /// <returns>Function value for the period</returns>
        public double GenerateF(DateTime date, double x)
        {
            //get demand for current date
            double request =
            (date.Month <= 3 || date.Month >= 11) ? GetFPeriodLow(x) :
            ((date.Month == 7) ? GetFPeriodJuly(x) : GetFPeriodHigh(x));

            return request;
        }

        public double getMinLimit(DateTime date)
        {
            //get demand for current date
            double request =
            (date.Month <= 3 || date.Month >= 11) ? this._pLowMin :
            ((date.Month == 7) ? this._pJulMin : this._pHighMin);

            return request;
        }

        public double getMaxLimit(DateTime date)
        {
            //get demand for current date
            double request =
            (date.Month <= 3 || date.Month >= 11) ? this._pLowMax :
            ((date.Month == 7) ? this._pJulMax : this._pHighMax);

            return request;
        }
    }
}